﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using HtmlAgilityPack;
using System.Globalization;
using Locator.LcboServices.PageLoaders.Services;
using Microsoft.Practices.CompositeWeb;
using Microsoft.Practices.EnterpriseLibrary.Logging;
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;
using Locator.LcboServices.PageLoaders.Properties;
using Locator.LcboServices.BusinessEntities;

namespace Locator.LcboServices.LcboProductsService.Services
{
    public class LcboProductServiceAgent : ILcboProductsService
    {
        private readonly IPageRetriever productDetailsPageRetriever;
        private readonly IPageRetriever productListPageRetriever;
        private readonly TraceManager traceManager;
        private readonly LogWriter logWriter;

        public LcboProductServiceAgent
            (
            [ServiceDependency]IPageRetrieverFactory pageRetrieverFactory
            )
        {
            this.productDetailsPageRetriever = pageRetrieverFactory.ProductDetailsPageRetriever;
            this.productListPageRetriever = pageRetrieverFactory.ProductListPageRetriever;
            this.logWriter = EnterpriseLibraryContainer.Current.GetInstance<LogWriter>();
            this.traceManager = new TraceManager(this.logWriter);
        }

        /// <summary>
        /// Gets the product description.
        /// </summary>
        /// <param name="lcboProductId">The lcbo product id.</param>
        /// <returns></returns>
        public string GetProductDescription(string lcboProductId)
        {
            StringBuilder result = new StringBuilder();
            HtmlDocument myPageSource;
            try
            {
                myPageSource = this.productDetailsPageRetriever.RetrievePageHtml(lcboProductId);
            }
            catch
            {
                myPageSource = this.productDetailsPageRetriever.RetrievePageHtml(String.Empty);
            }

            string textWithProductDescription = String.Empty;
            foreach (HtmlNode link in myPageSource.DocumentNode.SelectNodes("//td"))
            {
                if (link.InnerText.Contains(lcboProductId))
                {
                    textWithProductDescription = link.InnerText;
                }
            }

            List<string> linkText = new List<string>();
            string[] splitInnerText = textWithProductDescription.Split(new char[] { '\t', '\r', '\n', '|' }, StringSplitOptions.RemoveEmptyEntries);
            if (textWithProductDescription.Length > 0)
            {
                for (int i = 0; i < splitInnerText.Length - 1; i++)
                {
                    splitInnerText[i] = splitInnerText[i].Trim();
                    if (splitInnerText[i] != String.Empty)
                    {
                        linkText.Add(splitInnerText[i]);
                    }
                }

                int productNameIndex = 0;
                int containerFormatIndex = 2;
                TextInfo textInfo = CultureInfo.CurrentCulture.TextInfo;
                result.Append(textInfo.ToTitleCase(linkText[productNameIndex].ToLower(CultureInfo.CurrentCulture)));
                result.Append(" ");
                result.Append(linkText[containerFormatIndex]);
            }

            return result.ToString();
        }

        /// <summary>
        /// Gets all alcoholic products from the LCBO website.
        /// </summary>
        /// <returns></returns>
        public List<LcboProduct> GetAllProducts()
        {
            List<LcboProduct> result = new List<LcboProduct>();
            Dictionary<string, LcboProduct> resultDictionary = new Dictionary<string, LcboProduct>();
            HtmlDocument doc = this.productListPageRetriever.RetrievePageHtml(String.Empty);

            var productNodes = doc.DocumentNode.SelectNodes(String.Format("//a[@id[starts-with(.,'{0}')]]", Resources.ItemAnchorIdPrefix));
            if (productNodes != null)
            {
                foreach (HtmlNode link in productNodes)
                {
                    HtmlAttribute linkId = link.Attributes["id"];
                    string idName = linkId.Value;
                    string idNumber = idName.Substring(Resources.ItemAnchorIdPrefix.Length, idName.Length - Resources.ItemAnchorIdPrefix.Length - 1);
                    LcboProduct currentProduct;
                    if (!resultDictionary.ContainsKey(idNumber))
                    {
                        currentProduct = new LcboProduct();
                        resultDictionary.Add(idNumber, currentProduct);
                    }
                    else
                    {
                        currentProduct = resultDictionary[idNumber];
                    }

                    char idNameLastChar = idName[idName.Length - 1];
                    switch (idNameLastChar)
                    {
                        case '1':
                            TextInfo textInfo = CultureInfo.CurrentCulture.TextInfo;
                            currentProduct.Name = textInfo.ToTitleCase((link.InnerText.ToLower(CultureInfo.CurrentCulture)));
                            break;
                        case '2':
                            this.GetDetails(link.InnerText, currentProduct);
                            //if (currentProduct.Id == 0)
                            //{
                            //    resultDictionary.Remove(idNumber);
                            //}
                            break;
                        default:
                            break;
                    }
                }
            }

            // Filter out products with ID=0, which are non-alcoholic products, like gift bags
            var resultsWithNoId0Products = from p in resultDictionary.Values where p.Id != 0 select p;
            return resultsWithNoId0Products.ToList();
        }

        /// <summary>
        /// Gets the details.
        /// </summary>
        /// <param name="productText">The product text.</param>
        /// <param name="product">The product.</param>
        private void GetDetails(string productText, LcboProduct product)
        {
            string[] splitInnerText = productText.Split(new char[] { '\t', '\r', '\n', '|' }, StringSplitOptions.RemoveEmptyEntries);
            List<string> linkText = new List<string>();
            if (splitInnerText.Length > 0)
            {
                for (int i = 0; i < splitInnerText.Length - 1; i++)
                {
                    splitInnerText[i] = splitInnerText[i].Trim();
                    if (splitInnerText[i] != String.Empty)
                    {
                        linkText.Add(splitInnerText[i]);
                    }
                }
            }

            // Container
            product.Container = new LcboContainerFormat();
            string[] containerSizeSplit;
            if (linkText.Count >= 6)
            {
                if (!linkText[4].Contains("x"))
                {
                    containerSizeSplit = linkText[4].Split();
                    product.Container.NumberOfContainers = 1;
                    product.Container.ContainerCapacity = Convert.ToDecimal(containerSizeSplit[0]);
                }
                else
                {
                    containerSizeSplit = linkText[4].Split(new char[] { ' ', 'x' });
                    product.Container.NumberOfContainers = Convert.ToInt32(containerSizeSplit[0]);
                    product.Container.ContainerCapacity = Convert.ToDecimal(containerSizeSplit[1]);
                }

                // Id
                product.Id = Convert.ToInt32(linkText[3]);

                // Origin
                product.CountryOfOrigin = linkText[0];

                // Manufacturer
                product.Manufacturer = linkText[1];

                // Price
                string[] priceSplit = linkText[5].Split();
                if (priceSplit.Length >= 2)
                {
                    product.Price = Convert.ToDecimal(priceSplit[1]);
                }
            }

            //if (!String.IsNullOrEmpty(product.Name))
            //{
            //    StringBuilder result = new StringBuilder(product.Name);
            //    if (product.Container.NumberOfContainers > 1)
            //    {
            //        result.Append(String.Format("{0}x", product.Container.NumberOfContainers));
            //    }

            //    result.Append(String.Format("{0} ml", product.Container.ContainerCapacity));
            //    product.Name = result.ToString();
            //}

            //TODO: Case when items<5 i.e. it's not a drink
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (logWriter != null)
                {
                    logWriter.Dispose();
                }
            }
        }

        ~LcboProductServiceAgent()
        {
            Dispose(false);
        }
    }
}
